#ifndef MY_SERVER_H
#define MY_SERVER_H
#include "swoole.h"

#define swServer_get_thread(serv, reactor_id)    (&(serv->reactor_threads[reactor_id]))

enum swEventType
{
    //networking socket
    SW_EVENT_TCP             = 0,
    SW_EVENT_UDP             = 1,
    SW_EVENT_TCP6            = 2,
    SW_EVENT_UDP6            = 3,
    //tcp event
    SW_EVENT_CLOSE           = 4,
    SW_EVENT_CONNECT         = 5,
    //timer
    SW_EVENT_TIMER           = 6,
    //task
    SW_EVENT_TASK            = 7,
    SW_EVENT_FINISH          = 8,
    //package
    SW_EVENT_PACKAGE_START   = 9,
    SW_EVENT_PACKAGE_END     = 10,
    SW_EVENT_PACKAGE         = 11,
    SW_EVENT_SENDFILE        = 12,
    SW_EVENT_UNIX_DGRAM      = 13,
    SW_EVENT_UNIX_STREAM     = 14,
    //pipe
    SW_EVENT_PIPE_MESSAGE    = 15,
    //proxy
    SW_EVENT_PROXY_START     = 16,
    SW_EVENT_PROXY_END       = 17,
    SW_EVENT_CONFIRM         = 18,
    //event operate
    SW_EVENT_PAUSE_RECV,
    SW_EVENT_RESUME_RECV,
    //buffer event
    SW_EVENT_BUFFER_FULL,
    SW_EVENT_BUFFER_EMPTY,
};

enum swWorkerPipeType
{
	SW_PIPE_WORKER     = 0,
	SW_PIPE_MASTER     = 1,
	SW_PIPE_NONBLOCK   = 2,
};

typedef struct _swListenPort{
	int port;
	int sock;
	char host[SW_HOST_MAXSIZE];
	uint16_t backlog;
}swListenPort;

typedef struct
{
    long target_worker_id;
    swEventData data;
} swDispatchData;

struct _swFactory
{
    void *object;
    void *ptr; //server object
    int last_from_id;

    swReactor *reactor; //reserve for reactor

    int (*start)(struct _swFactory *);
    int (*shutdown)(struct _swFactory *);
    int (*dispatch)(struct _swFactory *, swDispatchData *);
    int (*finish)(struct _swFactory *, swSendData *);
    int (*notify)(struct _swFactory *, swDataHead *);    //send a event notify
    int (*end)(struct _swFactory *, int fd);
};

int swFactoryProcess_create(swFactory *factory, int worker_num);
swListenPort* swServer_add_port(swServer *serv, char *host, int port);
int swReactorThread_start(swServer *serv, swReactor *main_reactor_ptr);
int swServer_start(swServer *serv);
int swPort_listen(swListenPort *ls);
int swServer_free(swServer *serv);
void swServer_signal_init(swServer *serv);
void swWorker_signal_handler(int signo);

typedef struct{
	pid_t manager_pid;
	pid_t master_pid;
	swProcessPool task_workers;
	swProcessPool event_workers;
	
	//why session
	uint32_t session_round;
}swServerGS;

typedef struct _swReactorThread{
	pthread_t thread_id;
	swReactor reactor;
}swReactorThread;

struct _swServer{
	uint16_t reactor_num;
	uint16_t worker_num;
	uint16_t reactor_pipe_num;
	swFactory factory;
	swListenPort *listen_list;
	swServerGS *gs;
	swWorker *workers;

	void *ptr2;

	swReactorThread *reactor_threads;

	swConnection *connection_list;
	swSession *session_list;
	uint32_t max_connection;

	int (*onReceive)(swServer *, swEventData *);
	void (*onClose)(swServer *serv, swDataHead *);
	void (*onConnect)(swServer *serv, swDataHead *);

	int (*send)(swServer *serv, int fd, void *data, uint32_t length);
	int (*close)(swServer *serv, int fd, int reset);
};

typedef struct _swFactoryProcess
{
	swPipe *pipes;
} swFactoryProcess;


static sw_inline swWorker* swServer_get_worker(swServer *serv, uint16_t worker_id)
{
    //Event Worker
    if (worker_id < serv->worker_num)
    {
        return &(serv->gs->event_workers.workers[worker_id]);
    }

/*
    //Task Worker
    uint16_t task_worker_max = serv->task_worker_num + serv->worker_num;
    if (worker_id < task_worker_max)
    {
        return &(serv->gs->task_workers.workers[worker_id - serv->worker_num]);
    }

    //User Worker
    uint16_t user_worker_max = task_worker_max + serv->user_worker_num;
    if (worker_id < user_worker_max)
    {
        return &(serv->user_workers[worker_id - task_worker_max]);
    }

*/
    return NULL;
}

static sw_inline swConnection* swServer_connection_get(swServer *serv, int fd){
	return &serv->connection_list[fd];
}

//only one way to get worker
static sw_inline int swServer_worker_schedule(swServer *serv, int fd, swEventData *data){
	uint32_t key;

	key = fd;

	return key % serv->worker_num;
}

static sw_inline swSession* swServer_get_session(swServer *serv, uint32_t session_id)
{
	return &serv->session_list[session_id % SW_SESSION_LIST_SIZE];
}

static sw_inline swConnection *swServer_connection_verify(swServer *serv, int session_id)
{
    swSession *session = swServer_get_session(serv, session_id);
    int fd = session->fd;
	printf("-----fd is %d-----\n",fd);
    swConnection *conn = swServer_connection_get(serv, fd);
    if (!conn || conn->active == 0)
    {
        return NULL;
    }
    if (session->id != session_id || conn->session_id != session_id)
    {
        return NULL;
    }
    return conn;
}

/**
 * reactor_id: The fd in which the reactor.
 */
static sw_inline int swWorker_get_send_pipe(swServer *serv, int session_id, int reactor_id)
{
    int pipe_index = session_id % serv->reactor_pipe_num;
    /**
     * pipe_worker_id: The pipe in which worker.
     */
    int pipe_worker_id = reactor_id + (pipe_index * serv->reactor_num);
	printf("pipe_worker_id = %d====session_id = %d === reactor_id = %d\n",pipe_worker_id,session_id,reactor_id);
    swWorker *worker = swServer_get_worker(serv, pipe_worker_id);
    return worker->pipe_worker;
}

static sw_inline int swConnection_send(swConnection *conn, void *__buf, size_t __n, int __flags){
	int retval;

	retval = send(conn->fd, __buf, __n, __flags);
	return retval;
}

static sw_inline swConnection* swReactor_get(swReactor *reactor, int fd){
	//do not think of buffer
	swServer *serv = reactor->ptr;
	serv->connection_list[fd].fd = fd;

	return &serv->connection_list[fd];
}

static sw_inline void swReactor_del(swReactor *reactor, int fd){
	swConnection *socket = swReactor_get(reactor, fd);
	//socket->events = 0;
	socket->removed = 1;
}

static sw_inline int swServer_get_fd(swServer *serv, uint32_t session_id)
{
	return serv->session_list[session_id % SW_SESSION_LIST_SIZE].fd;
}

static sw_inline swConnection *swWorker_get_connection(swServer *serv, int session_id)
{
	int real_fd = swServer_get_fd(serv, session_id);
	swConnection *conn = swServer_connection_get(serv, real_fd);
	return conn;
}

#endif
